<?php
/**
 * eventsCalendar
 * 
 * Принимает id контейнера документов и выводит их в виде календаря. 
 *
 * @category 	snippet
 * @version 	2.0.4
 * @license 	bezumkin@yandex.ru
 * @internal	@properties 
 * @internal	@modx_category add

	[!eventsCalendar2!] 
	Сниппет для MODx Evolution, принимает id контейнера документов и выводит их в виде календаря.

	http://code.google.com/p/eventscalendar2/w/list

	Василий Наумкин.
	bezumkin@yandex.ru
	webstartpage.ru
**/

$classes_arr = get_declared_classes();

if (!in_array('eventsCalendar', $classes_arr)) {
class eventsCalendar {

    var $dateFormat = '%d %b %Y %H:%M';
    var $dateTV = 'event_date';
    var $tplEvent = 'tplEvent2';
    var $tplMain = 'tplCalendar2';
    var $id;
    
    var $month = '';
    var $year = '';

    var $calendar_id = 'Calendar';

    var $class_calendar = 'calendar';
    var $class_dow = 'dow';
    var $class_month = 'month';
    var $class_workday = 'workday';
    var $class_weekend = 'weekend';
    var $class_today = 'today';
    var $class_event = 'event';
    var $class_isevent = 'isevent';
    var $class_noevent = 'noevent';
    var $class_date = 'date';
    var $class_emptyday = 'emptyday';
    var $class_prev = 'prev';
    var $class_next = 'next';
    var $btn_prev = '&laquo;';
    var $btn_next = '&raquo;';    
    
    var $first_day = 1;
    
    var $lang = 'ru';
    var $local = array();
    var $dow_names = '';
    var $month_names = '';
    
    var $time_shift = 0;
        
    var $err = 0;
    var $show_errors = 1;
    
    function eventsCalendar() {
        $this->local['ru']['dow'] = 'Пн,Вт,Ср,Чт,Пт,Сб,Вс';
        $this->local['ru']['month'] = 'Январь,Февраль,Март,Апрель,Май,Июнь,Июль,Август,Сентябрь,Октябрь,Ноябрь,Декабрь';
        $this->local['en']['dow'] = 'Su,Mo,Tu,We,Th,Fr,Sa';
        $this->local['en']['month'] = 'January,February,March,April,May,June,July,August,September,October,November,December';

        $this->local['ru']['error']['no_id'] = 'Вы забыли указать id ресурса для выборки событий';
        $this->local['ru']['error']['no_action'] = 'Не указан метод для обработки ajax запроса';
        $this->local['ru']['error']['no_result'] = 'В указанном ресурсе нет подходящих документов';       
        $this->local['en']['error']['no_id'] = 'You forgot to specify the resource id for the event selection';
        $this->local['en']['error']['no_action'] = 'Not specify a method for handling ajax request';
        $this->local['en']['error']['no_result'] = 'The specified resource has no events';
    }
    
/*  Вывод ошибок    */
    function error($err) {
        return $local[$this->lang]['error'][$err];
    }

/*    Выводит календарь на заданный месяц и год    */
    function generateCalendar($month = '', $year = '') {
        //Это сильно модефицированный календарь, оригинал - вот тут: http://www.softtime.ru/scripts/calendar.php          
        if (empty($month)) {$month = date('m');}
        if (empty($year)) {$year = date('Y');}
        
        $time = mktime(0,0,0, $month, 1 ,$year);
        $dayofmonth = date('t', $time);     
        $day_count = 1;
            // 1. Первая неделя
            $num = 0;
        
        for ($i = 0; $i < 7; $i++) {
            // Вычисляем номер дня недели для числа
            $dayofweek = date('w', mktime(0, 0, 0, $month, $day_count, $year));
        
            // Приводим к числа к формату 1 - понедельник, ..., 6 - суббота
            if ($this->first_day == 1) {
            $dayofweek = $dayofweek - 1;
                if($dayofweek == -1) {$dayofweek = 6;}
            }
            
            if($dayofweek == $i) {
                // Если дни недели совпадают, заполняем массив $week числами месяца
                $week[$num][$i] = $day_count;
                $day_count++;
            }
            else {
                $week[$num][$i] = '';
            }
        }
        // 2. Последующие недели месяца
        while(true) {
            $num++;
        
            for($i = 0; $i < 7; $i++) {
              $week[$num][$i] = $day_count;
              $day_count++;
              // Если достигли конца месяца - выходим из цикла
              if($day_count > $dayofmonth) break;
            }
            // Если достигли конца месяца - выходим из цикла
            if($day_count > $dayofmonth) break;
        }
        // 3. Выводим содержимое массива $week в виде календаря
            $next_month = $month + 1; 
            if ($next_month == 13) {$next_month = 1; $next_year = $year + 1;}
            else {$next_year = $year;}
            
            $prev_month = $month - 1;
            if ($prev_month == 0) {$prev_month = 12; $prev_year = $year - 1;}
            else {$prev_year = $year;}    

        
        if (empty($this->dow_names)) {$this->dow_names = $this->local[$this->lang]['dow'];}
        if (empty($this->month_names)) {$this->month_names = $this->local[$this->lang]['month'];}
        
        $month_names = explode(',', $this->month_names);
            $month_name = $month_names[$month - 1];
        $dow_names = explode(',', $this->dow_names);
        
        
        $events = $this->getEvents($this->id, $month, $year);
        if (isset($this->err) && $this->show_errors == 1) {echo $this->local[$this->lang]['error'][$this->err];}
        
        if (strlen($month) == 1) {$month = '0'.$month;}
        
        $table = "<table class='$this->class_calendar'>";
        $table .= "<tr>
                    <td class='$this->class_prev'><a href='#' rel='$prev_month-$prev_year'>$this->btn_prev</a></td>
                    <td class='$this->class_month' colspan='5'>$month_name $year</td>
                    <td class='$this->class_next'><a href='#' rel='$next_month-$next_year'>$this->btn_next</a></td>
                   </tr><tr>";
          foreach ($dow_names as $v) {
              $table .= "<th class='$this->class_dow'>$v</th>";
          }
          foreach($week as $v) {
              $table .= "</tr><tr>";
                for($i = 0; $i < 7; $i++) {
                    if(!empty($v[$i])) {
                       
                       if (strlen($v[$i]) == 1) {$day = '0'.$v[$i];} else {$day = $v[$i];}
                       $date = $year.'-'.$month.'-'.$day;
                       if ($i == 5 || $i == 6) {
                           $class = $this->class_weekend;
                       }
                       else {
                           $class = $this->class_workday;
                       }
                       if ($date == strftime('%Y-%m-%d', time() + $this->time_shift*60*60)) {$class .= ' '.$this->class_today;}
                       if (!empty($events[$date])) {$class .= ' '.$this->class_isevent;}
                       else {$class .= ' '.$this->class_noevent;}
                       
                       $table .= "<td class='$class' id='".$this->calendar_id."_$v[$i]'>
                                   <div class='$this->class_date'>$v[$i]</div>
                                   <div class='$this->class_event'>$events[$date]</div>
                                  </td>
                                 ";
                   }
                   else $table .= "<td class='$this->class_emptyday'>&nbsp;</td>";
                }
          } 
          $table .= "</table>";
          
          return $table;
    }
    
/*  Вывод событий, принимает номер контейнера событий, месяц и год  */
    function getEvents($id = '', $month = '', $year = '') {
        global $modx;

        if (empty($id)) {$id = $modx->documentIdentifier;} // Если не указан ID - берем id текущего документа
        if (empty($month)) {$month = date('m');}
        if (empty($year)) {$year = date('Y');}
        if (strlen($month) == 1) {$month = '0'.$month;}


		/*	Узнаем ID потомков указанного контейнера	*/
		$tmp = $modx->getChildIds($id);
		if (empty($tmp)) {$this->err = 'no_result'; return;} // У документа нет потомков - это ошибка
		
		/*	Формируем из них строку для mysql*/
		foreach ($tmp as $v) {
			$str .= $v.',';
		}
		$ids_str = substr($str, 0, strlen($str) - 1); // Строка с нужными ID потомков через запятую

		/*	Фильтруем по дате все события из контейнера. Для этого нужно достать значение ТВ с датой	*/

		$sql = $modx->db->query("SELECT `id` FROM ".$modx->getFullTableName('site_tmplvars')."  WHERE `name` = '$this->dateTV'");
		$tvid = $modx->db->getValue($sql);

		$sql = $modx->db->query("SELECT `contentid` as `id`, `value` as `date` FROM ".$modx->getFullTableName('site_tmplvar_contentvalues')." WHERE `tmplvarid` = '$tvid' AND `contentid` IN ($ids_str)");
		$ids = $modx->db->makeArray($sql);
		if (empty($ids)) {return false;}
		
		/*  Получаем нужные события  */
	    $content = array();
        foreach ($ids as $v) {
			$d = strftime('%Y-%m', strtotime($v['date']));
			if ($d == "$year-$month") {
				$content[] = $modx->getTemplateVars('*','*', $v['id']);
			}
		}

        /*  Собираем массив в нужном формате для дальнейшей обработки   */
        if (empty($content)) {return false;} // Если данных за нужный месяц нет - возврат. Пусть выводится пустой календарь.
        else {
            $i = 0;
            foreach ($content as $tmp) {
                foreach ($tmp as $v) {
                    $arr[$i][$v['name']] = $v['value'];
                }
                $i++;
            }
        } 


        /*  Для начала достаем указаный шаблон оформления каждого события   */
        $tpl = $modx->getChunk($this->tplEvent);
        
        $i = 1;
        foreach ($arr as $v) {
            $placeholders = $values = array();
            /*  определяем переменные документа для подстановки в шаблон    */

            /*  Это для номера события, если событие за день оно не одно  */
            $date = strftime('%Y-%m-%d', strtotime($v[$this->dateTV]));
            if (isset($date2) && $date2 != $date) {$i = 1;}
            $date2 = $date;

			/*	Обязательные плейсхолдеры: урл, номер события и дата	*/
            $placeholders[] = '[+ec.url+]';
            $values[] = $modx->makeUrl($v['id']);
            $placeholders[] = '[+ec.num+]';
            $values[] = $i;
            $placeholders[] = '[+ec.date+]';
            $values[] = strftime($this->dateFormat, strtotime($v[$this->dateTV]));;
            

            foreach ($v as $k2 => $v2) {
                $placeholders[] = '[+ec.'.$k2.'+]';
                $values[] = $v2;
            }               
            /*  Стандартная замена плейсхолдеров на переменные в шаблонах modx  */
            $text = str_replace($placeholders, $values, $tpl);
            
            $dates[$date2] .= $text;
            
            $i++;
        }

            return $dates;

    }

    /*  Просто вывод основного шаблона на экран, по умолчанию   */    
    function output($tpl) {
        global $modx;
        
        $calendar = $this->generateCalendar($this->month,$this->year);
        
        $tpl = $modx->getChunk($tpl);
        
        $placeholders = array('[+ec.Calendar+]','[+ec.calendar_id+]','[+ec.class_calendar+]','[+ec.class_dow+]','[+ec.class_month+]',
                '[+ec.class_workday+]','[+ec.class_weekend+]','[+ec.class_today+]','[+ec.class_event+]','[+ec.class_isevent+]',
                '[+ec.class_noevent+]','[+ec.class_date+]','[+ec.class_emptyday+]','[+ec.class_prev+]','[+ec.class_next+]');
        $values = array($calendar,$this->calendar_id,$this->class_calendar,$this->class_dow,$this->class_month,
            $this->class_workday,$this->class_weekend,$this->class_today,$this->class_event,$this->class_isevent,
            $this->class_noevent,$this->class_date,$this->class_emptyday,$this->class_prev,$this->class_next);
        
        return str_replace($placeholders, $values, $tpl);
    }

}
}

/*  Конец класса, дальше обвязка, для его использования в MODx  */
$Cal = new eventsCalendar;

$Cal->id = $id; // ID документа, текущий по умолчанию
/*  Если заданы параметры при вызове сниппета - переопределяем стандартные  */
if (!empty($month)) {$Cal->month = $month;}
if (!empty($year)) {$Cal->year = $year;}
if (!empty($dateTV)) {$Cal->dateTV = $dateTV;}
if (!empty($dateFormat)) {$Cal->dateFormat = $dateFormat;}
if (!empty($tplEvent)) {$Cal->tplEvent = $tplEvent;}
if (!empty($tplMain)) {$Cal->tplMain = $tplMain;}
if (!empty($calendar_id)) {$Cal->calendar_id = $calendar_id;}
if (!empty($class_calendar)) {$Cal->class_calendar = $class_calendar;}
if (!empty($class_dow)) {$Cal->class_dow = $class_dow;}
if (!empty($class_month)) {$Cal->class_month = $class_month;}
if (!empty($class_workday)) {$Cal->class_workday = $class_workday;}
if (!empty($class_weekend)) {$Cal->class_weekend = $class_weekend;}
if (!empty($class_today)) {$Cal->class_today = $class_today;}
if (!empty($class_event)) {$Cal->class_event = $class_event;}
if (!empty($class_isevent)) {$Cal->class_isevent = $class_isevent;}
if (!empty($class_noevent)) {$Cal->class_noevent = $class_noevent;}
if (!empty($class_date)) {$Cal->class_date = $class_date;}
if (!empty($class_emptyday)) {$Cal->class_emptyday = $class_emptyday;}
if (!empty($class_prev)) {$Cal->class_prev = $class_prev;}
if (!empty($class_next)) {$Cal->class_next = $class_next;}
if (!empty($dow_names)) {$Cal->dow_names = $dow_names;}
if (!empty($month_names)) {$Cal->onth_names = $month_names;}
if (!empty($btn_prev)) {$Cal->btn_prev = $btn_prev;}
if (!empty($btn_next)) {$Cal->btn_next = $btn_next;}
if (!empty($lang) && !empty($Cal->local[$lang])) {$Cal->lang = $lang;}
if (!empty($first_day)) {$Cal->first_day = $first_day;}
if ($show_errors == '0') {$Cal->show_errors = 0;}
if (!empty($time_shift)) {$Cal->time_shift = $time_shift;}


/*  Если идет запрос через ajax - вызываем нужный метод и останавливаем работу  */
if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
        $action = $_POST['action'];
        if (!empty($action)) {
            switch($action) {
                case 'refreshCalendar':
                    $tmp = explode('-',$_POST['date']);
                    echo $Cal->generateCalendar($tmp[0], $tmp[1]);
                    break;
            }
        }
        else {
            echo $Cal->error('no_action');
        }
    die();
}
/*  Если обычный вызов - выводим календарь на экран */
else {
    echo $Cal->output($Cal->tplMain);
  //echo $Cal->getEvents($id); // Строчка для тестов
}
?>
